from collections import OrderedDict
from loguru import logger
from django.utils.functional import cached_property
from rest_framework import serializers
from rest_framework.fields import empty, FileField, SkipField
from rest_framework.relations import PKOnlyObject
from rest_framework.request import Request
from rest_framework.serializers import ModelSerializer
from rest_framework.settings import api_settings
from rest_framework.utils.field_mapping import get_nested_relation_kwargs
from rest_framework.utils.serializer_helpers import BindingDict


class CustomModelSerializer(ModelSerializer):
    """
    增强DRF的ModelSerializer,可自动更新模型的审计字段记录
    (1)仅当op_drf.generics.GenericAPIView的子类里使用时有效
    (2)非op_drf.generics.GenericAPIView里使用时, 与ModelSerializer作用一样,没有任何增强
    (3)self.request能获取到rest_framework.request.Request对象
    """

    # 修改人的审计字段名称, 默认modifier, 继承使用时可自定义覆盖
    modifier_field_name = "modifier"
    # 创建人的审计字段名称, 默认creator, 继承使用时可自定义覆盖
    creator_field_name = "creator"
    # 数据所属部门字段
    dept_belong_id_field_name = "dept_belong_id"
    # 添加默认时间返回格式
    create_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
    update_datetime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
    creator_name = serializers.SlugRelatedField(slug_field="name", source="creator", read_only=True)

    def __init__(self, instance=None, data=empty, request=None, **kwargs):
        super().__init__(instance, data, **kwargs)
        self.request: Request = request

    def save(self, **kwargs):
        return super().save(**kwargs)

    def create(self, validated_data):
        if self.context.get("request"):
            self.request = self.context.get("request")
        if self.request:
            username = self.get_request_username()
            if self.modifier_field_name in self.fields.fields:
                validated_data[self.modifier_field_name] = username
            if self.creator_field_name in self.fields.fields:
                validated_data[self.creator_field_name] = self.request.user
            if self.dept_belong_id_field_name in self.fields.fields:
                validated_data[self.dept_belong_id_field_name] = getattr(self.request.user, "dept_id", None)
        return super().create(validated_data)

    def update(self, instance, validated_data):
        # if self.request:
        #     if hasattr(self.instance, self.modifier_field_name):
        #         self.instance.modifier = self.get_request_username()
        return super().update(instance, validated_data)

    def get_request_username(self):
        if getattr(self.request, "user", None):
            return getattr(self.request.user, "username", None)
        return None

    @cached_property
    def fields(self):
        fields = BindingDict(self)
        for key, value in self.get_fields().items():
            fields[key] = value

        if not hasattr(self, "_context"):
            return fields
        is_root = self.root == self
        parent_is_list_root = self.parent == self.root and getattr(self.parent, "many", False)
        if not (is_root or parent_is_list_root):
            return fields

        try:
            request = self.request or self.context["request"]
        except KeyError:
            return fields
        params = getattr(request, "query_params", getattr(request, "GET", None))
        if params is None:
            pass
        try:
            filter_fields = params.get("_fields", None).split(",")
        except AttributeError:
            filter_fields = None

        try:
            omit_fields = params.get("_omit", None).split(",")
        except AttributeError:
            omit_fields = []

        existing = set(fields.keys())
        if filter_fields is None:
            allowed = existing
        else:
            allowed = set(filter(None, filter_fields))

        omitted = set(filter(None, omit_fields))
        for field in existing:
            if field not in allowed:
                fields.pop(field, None)
            if field in omitted:
                fields.pop(field, None)

        return fields

    def build_nested_field(self, field_name, relation_info, nested_depth):
        """
        Create nested fields for forward and reverse relationships.
        """

        class NestedSerializer(CustomModelSerializer):
            class Meta:
                model = relation_info.related_model
                depth = nested_depth - 1
                fields = "__all__"

        field_class = NestedSerializer
        field_kwargs = get_nested_relation_kwargs(relation_info)

        return field_class, field_kwargs

    def to_representation(self, instance):
        """
        重写序列化方法，使文件下载路径带前缀:/api
        @param instance:
        @return:
        """
        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            try:
                attribute = field.get_attribute(instance)
            except SkipField:
                continue

            # 跳过值为none的字段
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            if check_for_none is None:
                ret[field.field_name] = None
            else:
                if type(field) == FileField:
                    ret[field.field_name] = self.file_field_to_representation(attribute)
                    continue
                ret[field.field_name] = field.to_representation(attribute)

        return ret

    def file_field_to_representation(self, value):
        if not value:
            return None

        use_url = getattr(self, "use_url", api_settings.UPLOADED_FILES_USE_URL)
        if use_url:
            try:
                url = value.url
                url = "/api" + url
            except AttributeError:
                return None
            request = self.context.get("request", None)
            if request is not None:
                # 构建返回的文件绝对路径
                return request.build_absolute_uri(url)
            return url

        return value.name



class BaseSerializer(serializers.ModelSerializer):
    """
    格式化返回时间
    """

    create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
    update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)

    def build_nested_field(self, field_name, relation_info, nested_depth):
        """
        Create nested fields for forward and reverse relationships.
        """

        class NestedSerializer(BaseSerializer):
            class Meta:
                model = relation_info.related_model
                depth = nested_depth - 1
                fields = "__all__"

        field_class = NestedSerializer
        field_kwargs = get_nested_relation_kwargs(relation_info)

        return field_class, field_kwargs

    def to_representation(self, instance):
        """
        重写序列化方法，使文件下载路径带前缀:/api
        @param instance:
        @return:
        """
        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            try:
                attribute = field.get_attribute(instance)
            except SkipField:
                continue

            # 跳过值为none的字段
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            if check_for_none is None:
                ret[field.field_name] = None
            else:
                if type(field) == FileField:
                    ret[field.field_name] = self.file_field_to_representation(attribute)
                    # ret[field.field_name + "_url"] = "/api" + attribute.url
                    continue
                ret[field.field_name] = field.to_representation(attribute)

        return ret

    def file_field_to_representation(self, value):
        if not value:
            return None
        use_url = getattr(self, "use_url", api_settings.UPLOADED_FILES_USE_URL)
        if use_url:
            try:
                url = value.url
                url = "/api" + url
            except AttributeError:
                return None
            request = self.context.get("request", None)
            if request is not None:
                # 构建返回的文件绝对路径
                return request.build_absolute_uri(url)
            return url

        return value.name